package com.atguigu.gmall.list.service.impl;

import com.alibaba.fastjson.JSONObject;
import com.atguigu.gmall.list.dao.GoodsDao;
import com.atguigu.gmall.list.service.ListService;
import com.atguigu.gmall.model.list.*;
import com.atguigu.gmall.model.product.BaseCategoryView;
import com.atguigu.gmall.model.product.BaseTrademark;
import com.atguigu.gmall.model.product.SkuAttrValue;
import com.atguigu.gmall.model.product.SkuInfo;
import com.atguigu.gmall.product.client.ProductClientFeign;
import org.apache.lucene.search.join.ScoreMode;
import org.elasticsearch.action.search.SearchRequest;
import org.elasticsearch.action.search.SearchResponse;
import org.elasticsearch.client.RequestOptions;
import org.elasticsearch.client.RestHighLevelClient;
import org.elasticsearch.index.query.BoolQueryBuilder;
import org.elasticsearch.index.query.Operator;
import org.elasticsearch.index.query.QueryBuilders;
import org.elasticsearch.search.SearchHit;
import org.elasticsearch.search.SearchHits;
import org.elasticsearch.search.aggregations.Aggregation;
import org.elasticsearch.search.aggregations.AggregationBuilders;
import org.elasticsearch.search.aggregations.bucket.nested.ParsedNested;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedLongTerms;
import org.elasticsearch.search.aggregations.bucket.terms.ParsedStringTerms;
import org.elasticsearch.search.aggregations.bucket.terms.Terms;
import org.elasticsearch.search.builder.SearchSourceBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightBuilder;
import org.elasticsearch.search.fetch.subphase.highlight.HighlightField;
import org.elasticsearch.search.sort.SortOrder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author BrookPeng
 * @Date 2020/11/25 21:07
 * @Version 1.0
 */
@Service
public class ListServiceImpl implements ListService {

    @Autowired
    private GoodsDao goodsDao;

    @Autowired
    private ProductClientFeign productClientFeign;

    @Autowired
    private RedisTemplate redisTemplate;

    // 上架 保存库存信息到ES索引库
    // java代码连接ES索引库（学习内容）
    // kibana DSL 语句连接索引库
    @Override
    public void onSale(Long skuId) {

        Goods goods = new Goods();

        SkuInfo skuInfo = productClientFeign.getSkuInfo(skuId);

        // 1.SkuInfo
        goods.setId(skuInfo.getId());
        goods.setTitle(skuInfo.getSkuName());
        goods.setCreateTime(new Date());
        goods.setPrice(skuInfo.getPrice().doubleValue());
        goods.setDefaultImg(skuInfo.getSkuDefaultImg());

        // 2.分类
        BaseCategoryView categoryView = productClientFeign.getCategoryView(skuInfo.getCategory3Id());
        goods.setCategory1Id(categoryView.getCategory1Id());
        goods.setCategory2Id(categoryView.getCategory2Id());
        goods.setCategory3Id(categoryView.getCategory3Id());
        goods.setCategory1Name(categoryView.getCategory1Name());
        goods.setCategory2Name(categoryView.getCategory2Name());
        goods.setCategory3Name(categoryView.getCategory3Name());

        // 3.品牌
        BaseTrademark trademark = productClientFeign.getTrademark(skuInfo.getTmId());
        goods.setTmId(trademark.getId());
        goods.setTmName(trademark.getTmName());
        goods.setTmLogoUrl(trademark.getLogoUrl());

        // 4.平台属性值
        List<SkuAttrValue> attrValueList = productClientFeign.getAttrValueList(skuInfo.getId());
        List<SearchAttr> searchAttrList = attrValueList.stream().map((skuAttrValue) -> {
            SearchAttr searchAttr = new SearchAttr();
            // 平台属性ID
            searchAttr.setAttrId(skuAttrValue.getBaseAttrInfo().getId());
            // 平台属性名
            searchAttr.setAttrName(skuAttrValue.getBaseAttrInfo().getAttrName());
            // 平台属性值
            searchAttr.setAttrValue(skuAttrValue.getBaseAttrValue().getValueName());
            return searchAttr;
        }).collect(Collectors.toList());
        goods.setAttrs(searchAttrList);
//        goods.setId(666L);
//        goods.setTitle("保存第一个索引到索引库！");

        goodsDao.save(goods);
    }

    // 下架 删除库存信息从索引库
    @Override
    public void offSale(Long skuId) {

        goodsDao.deleteById(skuId);
    }

    // 商品热度更新
    @Override
    public void hotScore(Long skuId) {

        // 1、更新Redis 使用ZSet(有序集合)数据类型（例：小明的语文成绩，数学成绩，英语成绩。。。）
        String hotScore = "hotScore";
        // 使用原子性操作，自增方法incrementScore(k1, k2, 自增量)
        Double aDouble = redisTemplate.opsForZSet().incrementScore(hotScore, skuId, 1);
        if (aDouble % 10 == 0) {
            Optional<Goods> byId = goodsDao.findById(skuId);
            Goods goods = byId.get();
            goods.setHotScore(Math.round(aDouble));
            goodsDao.save(goods);
            System.out.println("ES已更新，分数为：" + aDouble);
        } else {
            System.out.println("ES未更新，分数为：" + aDouble);
        }
    }

    @Autowired
    private RestHighLevelClient restHighLevelClient;

    // 开始搜索
    @Override
    public SearchResponseVo list(SearchParam searchParam) {

        SearchRequest searchRequest = new SearchRequest("goods");
        // 1:构建条件对象
        SearchSourceBuilder sourceBuilder = buildSourceBuilder(searchParam);
        searchRequest.source(sourceBuilder);
        SearchResponseVo responseVo = null;
        try {
            // 2:执行搜索
            SearchResponse searchResponse =
                    restHighLevelClient.search(searchRequest, RequestOptions.DEFAULT);
            // 3:解析结果
            responseVo = buildSearchResponse(searchResponse);
            // 4:总页数计算
            // 总记录数
            // 当前页
            responseVo.setPageNo(searchParam.getPageNo());
            // 每页数
            responseVo.setPageSize(searchParam.getPageSize());
            // 总页数
            responseVo.setTotalPages((responseVo.getTotal() + responseVo.getPageSize() - 1) / responseVo.getPageSize());
        } catch (IOException e) {
            e.printStackTrace();
        }

        return responseVo;
    }

    // 解析结果
    private SearchResponseVo buildSearchResponse(SearchResponse searchResponse) {

        SearchResponseVo vo = new SearchResponseVo();
        SearchHits hits = searchResponse.getHits();
        // 1:总记录数
        vo.setTotal(hits.getTotalHits());
        SearchHit[] hits1 = hits.getHits();
        if (null != hits1 && hits1.length > 0) {
            // 2:商品结果集
            List<Goods> goodsList = Arrays.stream(hits1).map((hit) -> {
                // 普通搜索商品的对象 名称是普通名称
                Goods goods = JSONObject.parseObject(hit.getSourceAsString(), Goods.class);
                // 3:高亮
                Map<String, HighlightField> highlightFields = hit.getHighlightFields();
                if (null != highlightFields && highlightFields.size() > 0) {
                    HighlightField title = highlightFields.get("title");
                    if (null != title) {
                        // 有高亮优先使用高亮名称 没有就使用原来的普通名称
                        goods.setTitle(title.fragments()[0].toString());
                    }
                }
                return goods;
            }).collect(Collectors.toList());
            vo.setGoodsList(goodsList);
        }

        Map<String, Aggregation> aggregationMap = searchResponse.getAggregations().asMap();

        // 4:品牌
        ParsedLongTerms tmIdAgg = (ParsedLongTerms) aggregationMap.get("tmIdAgg");
        // private List<SearchResponseTmVo> trademarkList;
        List<SearchResponseTmVo> searchResponseTmVoList = tmIdAgg.getBuckets().stream().map((bucket) -> {
            // 品牌对象
            SearchResponseTmVo searchResponseTmVo = new SearchResponseTmVo();
            // 品牌ID
            searchResponseTmVo.setTmId(Long.parseLong(bucket.getKeyAsString()));
            // 品牌名称
            ParsedStringTerms tmNameAgg = ((Terms.Bucket) bucket).getAggregations().get("tmNameAgg");
            searchResponseTmVo.setTmName(tmNameAgg.getBuckets().get(0).getKeyAsString());
            // 品牌Logo
            ParsedStringTerms tmLogoUrlAgg = ((Terms.Bucket) bucket).getAggregations().get("tmLogoUrlAgg");
            searchResponseTmVo.setTmLogoUrl(tmLogoUrlAgg.getBuckets().get(0).getKeyAsString());
            return searchResponseTmVo;
        }).collect(Collectors.toList());
        vo.setTrademarkList(searchResponseTmVoList);
        // 5:平台
        ParsedNested attrsAgg = (ParsedNested) aggregationMap.get("attrsAgg");
        ParsedLongTerms attrIdAgg = attrsAgg.getAggregations().get("attrIdAgg");
        // private List<SearchResponseAttrVo> attrsList = new ArrayList<>();
        List<SearchResponseAttrVo> attrsList = attrIdAgg.getBuckets().stream().map((bucket) -> {
            SearchResponseAttrVo attrVo = new SearchResponseAttrVo();
            // 平台属性ID
            attrVo.setAttrId(Long.parseLong(((Terms.Bucket) bucket).getKeyAsString()));
            // 平台属性名称
            ParsedStringTerms attrNameAgg = ((Terms.Bucket) bucket).getAggregations().get("attrNameAgg");
            attrVo.setAttrName(attrNameAgg.getBuckets().get(0).getKeyAsString());
            // 平台属性名称的值
            ParsedStringTerms attrValueAgg = ((Terms.Bucket) bucket).getAggregations().get("attrValueAgg");
            // private List<String> attrValueList = new ArrayList<>();
            List<String> stringList = attrValueAgg.getBuckets().stream().map((bucket1) -> {
                return ((Terms.Bucket) bucket1).getKeyAsString();
            }).collect(Collectors.toList());
            attrVo.setAttrValueList(stringList);

            return attrVo;
        }).collect(Collectors.toList());
        vo.setAttrsList(attrsList);
        return vo;
    }

    // 构建条件对象
    private SearchSourceBuilder buildSourceBuilder(SearchParam searchParam) {

        // 9个参数 6部分数据
        // 1:关键词             基础 必填部分 默认值
        String keyword = searchParam.getKeyword();
        // 构建查询条件对象
        SearchSourceBuilder sourceBuilder = new SearchSourceBuilder();
        // 构建组合查询条件对象 boolQueryBuilder.must()/.filter() = and ; .should() = or ; .mustnot() = not
        BoolQueryBuilder boolQueryBuilder = QueryBuilders.boolQuery();
        if (!StringUtils.isEmpty(keyword)) {
            // 组合条件组装必填值
            // matchQuery : 我是中国人
            // 第一步： 先分词，我 是 中国 中国人 国人
            // 第二步： like %我% AND %是% AND %中国% AND %中国人% AND %国人%
            boolQueryBuilder.must(QueryBuilders.matchQuery("title", keyword).operator(Operator.AND));
            // sourceBuilder.query(QueryBuilders.matchQuery("title", keyword));
        } else {
            // 意外情况下keyword为空时，可以任意处理
            boolQueryBuilder.must(QueryBuilders.matchAllQuery()); // like 分词
        }
        // ========================
        // 2:品牌               过滤条件
        String trademark = searchParam.getTrademark();
        if (!StringUtils.isEmpty(trademark)) {
            String[] split = StringUtils.split(trademark, ":");
            boolQueryBuilder.filter(QueryBuilders.termQuery("tmId", split[0]));
        }
        // 3:三级分类           过滤条件
        Long category1Id = searchParam.getCategory1Id();
        if (null != category1Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category1Id", category1Id));
        }
        Long category2Id = searchParam.getCategory2Id();
        if (null != category2Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category2Id", category2Id));
        }
        Long category3Id = searchParam.getCategory3Id();
        if (null != category3Id) {
            boolQueryBuilder.filter(QueryBuilders.termQuery("category3Id", category3Id));
        }
        // 4:平台属性数组       过滤条件
        String[] props = searchParam.getProps();
        if (null != props && props.length > 0) {
            // lambda写法如下注释
            /*Arrays.stream(props).forEach(s -> {
            });*/

            // 新的内层组合对象
            BoolQueryBuilder boolQueryBuilder1 = QueryBuilders.boolQuery();
            for (String prop : props) {
                // prop = 平台属性的ID：平台属性的值的名称：平台属性的名称
                String[] split = prop.split(":");
                // split[0]=平台属性的ID
                // split[1]=平台属性的名称
                // split[2]=平台属性的值
                // 组合对象
                BoolQueryBuilder boolQueryBuilder2 = QueryBuilders.boolQuery();
                boolQueryBuilder2.must(QueryBuilders.termQuery("attrs.attrId", split[0]));
                boolQueryBuilder2.must(QueryBuilders.termQuery("attrs.attrValue", split[1]));

                boolQueryBuilder1.must(QueryBuilders.nestedQuery("attrs", boolQueryBuilder2, ScoreMode.None));
            }
            boolQueryBuilder.filter(boolQueryBuilder1);
        }
        //
        sourceBuilder.query(boolQueryBuilder); // 只能设置一次，否则会覆盖前面的设置
        // ========================
        // 5:排序        （综合1）（价格2）（新品3）
        String order = searchParam.getOrder();
        if (!StringUtils.isEmpty(order)) {
            String[] split = StringUtils.split(order, ":");
            String s = null;
            switch (split[0]) {
                case "1":
                    s = "hotScore";
                    break;
                case "2":
                    s = "price";
                    break;
                case "3":
                    s = "createTime";
                    break;
            }
            sourceBuilder.sort(s, split[1].equalsIgnoreCase("desc") ? SortOrder.DESC : SortOrder.ASC);
        } else {
            // 默认 （热度）排序
            sourceBuilder.sort("hotScore", SortOrder.DESC);
        }
        // 6:分页
        Integer pageNo = searchParam.getPageNo();
        Integer pageSize = searchParam.getPageSize();
        sourceBuilder.from((pageNo - 1) * pageSize);// 开始行
        sourceBuilder.size(pageSize);// 每页条数
        // 7:高亮
        HighlightBuilder highlightBuilder = new HighlightBuilder();
        highlightBuilder.field("title").preTags("<font style='color:red'>").postTags("<font/>");
        HighlightBuilder highlighter = sourceBuilder.highlighter();
        // highlighter.preTags("<font style='color:green'>");
        sourceBuilder.highlighter(highlightBuilder);
        // 8:品牌进行聚合（以去重显示）
        // 品牌ID为父聚合，品牌名称为子聚合，品牌的图片为子聚合
        sourceBuilder.aggregation(
                AggregationBuilders.terms("tmIdAgg").field("tmId")
                        .subAggregation(AggregationBuilders.terms("tmNameAgg").field("tmName"))
                        .subAggregation(AggregationBuilders.terms("tmLogoUrlAgg").field("tmLogoUrl"))
        );
        // 9:平台属性聚合 嵌套
        sourceBuilder.aggregation(
                AggregationBuilders.nested("attrsAgg", "attrs")
                        .subAggregation(AggregationBuilders.terms("attrIdAgg").field("attrs.attrId")
                                .subAggregation(AggregationBuilders.terms("attrNameAgg").field("attrs.attrName"))
                                .subAggregation(AggregationBuilders.terms("attrValueAgg").field("attrs.attrValue")))

        );
        return sourceBuilder;
    }
}
